package com.yy.job.core.dataprocess;

import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import com.yy.job.common.utils.date.YYDateUtils;
import com.yy.job.constant.JobConstant;
import com.yy.job.core.config.xml.JobXmlConfig;
import com.yy.job.core.domain.JobDomain;
import com.yy.job.core.run.worker.ModuleWorker;
import com.yy.job.spring.YYSpringBeanUtils;

/**
 * 状态为(F,L)的job处理器
 * 
 * @author eleven.li
 *
 */
public class PendingJobDataProcess extends BatchJobDataProcess {

	private Logger log = LoggerFactory.getLogger(PendingJobDataProcess.class);

	private long lastExecuteTime = 0L;

	private boolean pendingJobThreadRunning = false;

	public PendingJobDataProcess(ModuleWorker jobBoss) {
		super(jobBoss);
	}
	
	@Override
	public void execute(int runCount) {
		log.info("start PendingJobDataProcess, moduleWorker name={}", this.moduleWorker.getModuleWorkerName());

		// 获取上次执行时间和当前时间相差分钟数
		int failJobExecDurMinutes = YYDateUtils.getIntervalMinute(this.lastExecuteTime, System.currentTimeMillis());
		
		// 相隔时间超过60分钟,需要开始处理失败job
		// 相隔时间超过5分钟但是isFailJobThreadRuning=false,则表示需要开始处理失败job(设置为1分钟是为了避免太频繁的重试)
		if (failJobExecDurMinutes > 60 || (failJobExecDurMinutes > 1 && !this.pendingJobThreadRunning)) {
			this.pendingJobThreadRunning = true;
			// 异步处理，不希望异常的任务干扰到正常任务的执行速度
//			(new Thread(() -> {
//				try {
//					// 执行失败的job
//					this.performFailJobs();
//				} catch (Exception ex) {
//					log.error("execute performFailJobs failed", ex);
//				} finally {
//					this.pendingJobThreadRunning = false;
//					this.lastExecuteTime = System.currentTimeMillis();
//				}
//			})).start();
			
			try {
				// 执行失败的job,不做异步执行
				this.performFailJobs(runCount);
			} catch (Exception ex) {
				log.error("execute performFailJobs failed", ex);
			} finally {
				this.pendingJobThreadRunning = false;
				this.lastExecuteTime = System.currentTimeMillis();
			}
		}
		
		log.info("end PendingJobDataProcess, moduleWorker name={}", this.moduleWorker.getModuleWorkerName());
	}

	/**
	 * 执行失败的job
	 */
	private void performFailJobs(int runCount) {
		List<JobDomain> failJobList = null;
		Long lastJobId = Long.valueOf(0L);
		while (true) {
			// 最多查询距今4天以内的数据
			Date pendingBackDate = DateUtils.addDays(new Date(), -JobConstant.PENDING_BACK_DAYS);
			
			// 获取失败的任务列表(默认100条)，根据id升序查询，回溯最近PENDING_BACK_DAYS天的数据
			failJobList = YYSpringBeanUtils.getJobDomainService().listPendingFailJobByPage(
				this.moduleWorker.getProjectCode(),
				this.moduleWorker.getModuleJobConfig().getModuleCode(), lastJobId, pendingBackDate, JobConstant.PAGE_SIZE);
			log.info("listPendingFailJobByPage, projectCode={}, moduleCode={}, resultJobListSize={}", 
					this.moduleWorker.getProjectCode(),
					this.moduleWorker.getModuleJobConfig().getModuleCode(), failJobList.size());
			
			// 如果没有失败的，则跳出，等待下次启动
			if (CollectionUtils.isEmpty(failJobList)) {
				break;
			}

			// 放入worker,准备执行
			for (JobDomain job : failJobList) {
				if (job.getId().longValue() > lastJobId.longValue()) {
					lastJobId = job.getId();
				}
				if (checkJob(job) && lockJobByVersion(job, runCount)) {
					job.setRunCount(runCount);
					this.moduleWorker.assignJobToWoker(job);
				}
			}
			
			// 如果最近一次查出来的数量不满足分页数量的一半，则执行完毕退出
			if(failJobList.size() < JobConstant.BREAK_PAGE_SIZE) {
				break;
			}
		}
	}

	/**
	 * 校验任务是否可以执行
	 * 
	 * @param jobDomain
	 * @return
	 */
	private boolean checkJob(JobDomain jobDomain) {
		if (jobDomain == null || jobDomain.getJobStatus() == null || StringUtils.isBlank(jobDomain.getJobCode())) {
			return false;
		}
		
		long nowTime = System.currentTimeMillis();
		switch (jobDomain.getJobStatus()) {
		case FAIL:
			// job状态为FAIL
			// 创建时间超过4天以上或者执行次数超过10的任务，将不会进行重试。
			// 可以前往控制台手动启动。控制台重启之后，会把执行次数重置为0
			if (jobDomain.getExecCount().intValue() > JobConstant.RETRY_COUNT
					|| YYDateUtils.getIntervalDay(jobDomain.getCreatedTime().getTime(), nowTime) >= JobConstant.PENDING_BACK_DAYS) {
				log.debug(
						"Job can't be executed, because job has been executed 10 times or job created at 4 days ago, job={}",
						jobDomain.toString());
				return false;
			}
			
			// 获取当前次数所设定的最大间隔时间
			int delayMins = JobConstant.FAIL_JOB_DELAY_TIME_CFG.get(jobDomain.getExecCount()) == null ?
				JobConstant.MAX_DELAY_MINUTES : JobConstant.FAIL_JOB_DELAY_TIME_CFG.get(jobDomain.getExecCount());
			if (YYDateUtils.getIntervalMinute(jobDomain.getExecTime().getTime(), nowTime) < delayMins) {
				log.debug(
						"Job can't be executed, because job executed fail time distance now not achieve config time interval , job={}",
						jobDomain.toString());
				return false;
			}
			return true;
		case LOCK:
			// job状态为LOCK, 会存在一直处于lock状态但是没有执行的情况，需要释放重新执行
			JobXmlConfig jobConfig = this.moduleWorker.getModuleJobConfig().findByJobCode(jobDomain.getJobCode());

			// 锁定任务的默认释放时间
			int lockReleaseMinutes = 60;
			if (StringUtils.isNotBlank(jobConfig.getLockTime())) {
				lockReleaseMinutes = Integer.parseInt(jobConfig.getLockTime());
			}

			// 存在一种极端情况，任务创建成功且为LOCK,但是任务加入任务池失败
			if(jobDomain.getExecTime() == null
				&& YYDateUtils.getIntervalMinute(jobDomain.getModifiedTime().getTime(), nowTime) < lockReleaseMinutes) {
				log.debug(
						"Job can't be executed, because job exec time is null , job={}, lockReleaseMinutes={}",
						jobDomain.toString(), Integer.valueOf(lockReleaseMinutes));
				return false;
			}
			// 当执行时间距现在时间的间隔 超过设置的锁定时间，则重新执行
			// 此处用modifiedTime判断，是因为存在一种情况，当任务重启之后，其中一个线程将任务变成L,另外一个线程读到的是L的数据，有可能会重复执行。所以需要用修改时间做下判断
			else if ((jobDomain.getExecTime()!= null 
				&& YYDateUtils.getIntervalMinute(jobDomain.getExecTime().getTime(), nowTime) < lockReleaseMinutes)
				|| YYDateUtils.getIntervalMinute(jobDomain.getModifiedTime().getTime(), nowTime) < lockReleaseMinutes) {
				log.debug(
						"Job can't be executed, because job locked time distance now not achieve config time interval , job={}, lockReleaseMinutes={}",
						jobDomain.toString(), Integer.valueOf(lockReleaseMinutes));
				return false;
			}
			return true;
		case CREATE:
			// job状态为CREATE
			// 不执行
			return true;
		case SUCCESS:
			// job状态为SUCCESS
			// 不执行
			return false;
		case CLOSE:
			// job状态为CLOSE
			// 不执行
			return false;
		}

		return false;
	}
	
}
